#pragma once

#include "IBitmapPicture.h"

enum OccupyMask { 
	kOccupiedByOthersMask = 1, 
	kOccupiedBySelfMask = 2,
	kOutsideBoundsMask = 0x3c,
	kOccupiedBySpeck = 0x40
};

struct SubpixelInfo 
{
	float a, b, colors[3];
	short int gradientFormulaID;
	char occupiedState, isColorsReady;
};

///////////////////////
// forward declarations
struct AveragingMatrix;
struct LevelPoints;

class SubpixelInfoProvider
{
	const BYTE* mOriginalData; // weak reference to original picture
	int mWidth, mHeight, mOffsetX, mOffsetY;

	float mRadius, mRadiusSquared;
	int mRadius2Int;

	SubpixelInfo *mInfoBuffer;
	int mTwiceWidth, mTwiceHeight, mWidth4;
	int mMaxTwiceX, mMaxTwiceY;
	int mMaxUncheckedX, mMaxUncheckedY;

	float mDebugFriendlyScale;

	short int mGradientFormulaID, mLastCustomFormulaID;
	float mColorGradientFactors[3];

	AveragingMatrix* mMatrixes[4];
	const AveragingMatrix* mCurrentAveragingMatrix;
	void *mAllMatrixesElements;

	int mMinDx, mMaxDx, mMinDy, mMaxDy;

public:
	SubpixelInfoProvider(const IBitmapPicture&, float blurRadius);
	~SubpixelInfoProvider();

	const SubpixelInfo* GetInfo(int twiceX, int twiceY);
	void SetColorGradientFactors(const float* threeFactors);

	const SubpixelInfo* GetInfoBuffer() const;

	bool IsSafeToGetUpperRightNeighbor(const SubpixelInfo* referencePtr,
		int safetyMargin) const;
	const float* GetColorsAtGradientPoint(const SubpixelInfo* referencePtr);

private:
	const SubpixelInfo* GetMarginInfo(bool atLeft, bool atRight,
		bool atTop, bool atBottom);

	void UpdateGradientABGeneral(const float* p, const float* px,
		const float* py, const float* pxy, SubpixelInfo* outInfo);
	void UpdateGradientABCustomColor(const float* p, const float* px,
		const float* py, const float* pxy, SubpixelInfo* outInfo) const;

	void InitializeAveragingMatrixes();
	void AdoptMatrixes(const AveragingMatrix* fourMatrixes);
	LevelPoints* CalculateMatrixCentral(LevelPoints* startingPoints);
	LevelPoints* CalculateMatrixesXandY(LevelPoints* startingPoints);
	LevelPoints* CalculateMatrixXY(LevelPoints* startingPoints);
	void NormalizeMatrixes();
	void AddOffset(LevelPoints*, int dx, int dy);
	float CalculateCellIntegral(double centerX, double centerY) const;

	const float* SmoothUnchecked(SubpixelInfo*, const BYTE*);
	const float* SmoothWithBoundsCheck(SubpixelInfo*, const BYTE*);
};

